﻿using System;
using System.Windows;
using FreshMeat.Xaml;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace XamlTest
{
   [TestClass]
   public class AttachedBehaviorRegistryTest
   {
      [TestMethod]
      public void GetAndReleaseTest()
      {
         var registry = new AttachedBehaviorRegistry();

         var instance = (MockBehavior)registry.GetInstance(typeof(MockBehavior), new MockObject(50));

         Assert.AreEqual(1, instance.AttachCount, "OnAttach should have occurred due to creation.");
         Assert.AreEqual(0, instance.DetachingCount, "OnDetaching should not have occurred.");
         Assert.AreEqual(1, registry.InstanceCount, "Should contain only the instance we just created.");

         registry.ReleaseInstance(instance);

         Assert.AreEqual(1, instance.AttachCount, "OnAttach should not have changed.");
         Assert.AreEqual(1, instance.DetachingCount, "OnDetaching should have occurred due to release.");
         Assert.AreEqual(0, registry.InstanceCount, "Should not contain any instances since we just removed the one it had.");
      }

      [TestMethod]
      public void CleanupTest()
      {
         var registry = new AttachedBehaviorRegistry();

         // Create massive object that should be aggresively garbage collected
         var instance = (MockBehavior)registry.GetInstance(typeof(MockBehavior), new MockObject(50000000));

         Assert.AreEqual(1, instance.AttachCount, "OnAttach should have occurred due to creation.");
         Assert.AreEqual(0, instance.DetachingCount, "OnDetaching should not have occurred.");
         Assert.AreEqual(1, registry.InstanceCount, "Should contain only the instance we just created.");

         // Force a garbage collection which should collect the massive MockObject we created.
         GC.Collect();
         // Now force a cleanup, which should notice the associated object has been garbage collected
         // and should therefore release it and in the process should call Detach.
         registry.Cleanup();

         Assert.AreEqual(1, instance.AttachCount, "OnAttach should not have changed.");
         Assert.AreEqual(1, instance.DetachingCount, "OnDetaching should have occurred due to cleanup.");
         Assert.AreEqual(0, registry.InstanceCount, "Should not contain any instances since we just removed the one it had.");
      }

      [TestMethod]
      public void AutomaticCleanupTest()
      {
         var registry = new AttachedBehaviorRegistry();

         // Create 3x the number of objects indicated by the creates-to-cleanup ratio,
         // which should trigger 3 cleanups.
         for (var i = 0; i < registry.CreatesToCleanupRatio * 3; i++)
            registry.GetInstance(typeof(MockBehavior), new MockObject(50));
         Assert.AreEqual(3, registry.CleanupCount, "Should have done 3 cleanups so far.");
      }

      class MockObject : DependencyObject
      {
         public MockObject(int sizeInBytes)
         {
            data = new byte[sizeInBytes];
         }

         // ReSharper disable NotAccessedField.Local
         byte[] data;
         // ReSharper restore NotAccessedField.Local
      }

      public class MockBehavior : AttachedBehavior<MockBehavior, DependencyObject>
      {
         public int AttachCount;
         public int DetachingCount;

         protected override void OnAttached()
         {
            AttachCount++;
         }

         protected override void OnDetaching()
         {
            DetachingCount++;
         }
      }
   }
}
